(function (fruit, global, document, undefined) {
    //Give name space
    /*//Create namespace
     extend(fruit, {
     Event:null,
     UIEvent:null,
     MouseEvent:null,
     KeyboardEvent:null
     });*/
    function extend(inSource, inTarget) {
        var prop;
        for (prop in inTarget) {
            inSource[prop] = inTarget[prop];
        }
    }

    function copyPrototype(inCurrentCls, inParentCls) {
        inCurrentCls.prototype = inParentCls.prototype;
        return new inCurrentCls();
    }

    //RootClass:
    fruit.Class = function () {
    };
    //Event:
    fruit.Event = function (inEvent) {
        if (inEvent) {
            this.originalEvent = inEvent;
            this.type = inEvent.type;
        }
    };
    fruit.Event.prototype = copyPrototype(fruit.Event, fruit.Class);
    extend(fruit.Event.prototype, {
        constructor:fruit.Event,
        stopPropagation:function () {
            var event = this.originalEvent;
            if ('stopPropagation' in event) {
                event.stopPropagation();
            } else {
                event.cancelBubble = true;
            }
        },
        preventDefault:function () {
            var event = this.originalEvent;
            if ('preventDefault' in event) {
                event.preventDefault();
            } else {
                event.returnValue = false;
            }
        }
    });
    //UIEvent:
    fruit.UIEvent = function (inEvent) {
        if (inEvent) {
            fruit.Event.call(this, inEvent);
            this.target = inEvent.target || inEvent.srcElement || document;
        }
    };
    fruit.UIEvent.prototype = copyPrototype(fruit.UIEvent, fruit.Event);
    extend(fruit.UIEvent.prototype, {
        constructor:fruit.UIEvent,
        getDocument:function () {
            var target = this.getTarget();
            return target.ownerDocument || document;
        }
    });
    //MouseEvent:
    fruit.MouseEvent = function (inEvent) {
        fruit.UIEvent.call(this, inEvent);
    };
    fruit.MouseEvent.prototype = copyPrototype(fruit.MouseEvent, fruit.UIEvent);
    extend(fruit.MouseEvent.prototype, {
        constructor:fruit.MouseEvent,
        getPageX:function () {
            var doc = this.getDocument();
            var event = this.originalEvent;
            return event.pageX || (event.clientX + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - 2) || 0;
        },
        getPageY:function (inEvent) {
            var doc = this.getDocument();
            var event = this.originalEvent;
            return event.pageY || (event.clientY + (doc.documentElement.scrollTop || doc.body.scrollTop) - 2) || 0;
        }
    });
    //KeyboardEvent:
    fruit.KeyboardEvent = function (inEvent) {
        fruit.UIEvent.call(this, inEvent);
    };
    fruit.KeyboardEvent.prototype = copyPrototype(fruit.KeyboardEvent, fruit.UIEvent);
    extend(fruit.KeyboardEvent.prototype, {
        constructor:fruit.KeyboardEvent,
        getKeyCode:function () {
            var event = this.originalEvent;
            return ('keyCode' in event) ? event.keyCode : (event.charCode || event.which || 0);
        }
    });
    global.fruit = fruit;
}(fruit, window, document, undefined));
(function (fruit, global, document, undefined) {
    var each = function (obj, iterator) {
        if (obj && iterator) {
            if (obj.forEach) {
                obj.forEach(iterator);
            }
            else {
                for (var key in obj) {
                    var value = obj[key];
                    iterator(value, key);
                }
            }
        }
    };
    var getHash = (function () {
        var identity = 1;
        return function (obj) {
            if (obj) {
                var hash = obj.$id;
                if (!hash) {
                    hash = obj.$id = identity++;
                }
                return hash;
            }
            else {
                return null;
            }
        }
    })();
    var EventHandler = fruit.define({
        properties:{
            capture:{
                value:false
            }
        },
        methods:{
            init:function (type, capture) {
                this._type = type;
                this._capture = capture || false;
                this._map = {};
            },
            activate:function () {
                if (!this._active) {
                    this._active = true;
                    eventEngine.addEventListener(document, this._type, eventEngine.dispatch, this._capture);
                    eventEngine._handlerMap[this._type] = this;
                }
            },
            deactivate:function () {
                if (this._active) {
                    this._active = false;
                    eventEngine.removeEventListener(document, this._type, eventEngine.dispatch, this._capture);
                    eventEngine._handlerMap[this._type] = null;
                }
            },
            upon:function (owner, callback, context, meta) {
                var map = this._map;
                var hash = getHash(owner);
                var items = map[hash] = map[hash] || [null];
                if (callback) {
                    items[0] = {
                        owner:owner,
                        callback:callback,
                        context:context,
                        meta:meta
                    };
                }
                else {
                    items[0] = null;
                }
            },
            on:function (owner, callback, context, meta) {
                var map = this._map;
                var hash = getHash(owner);
                var items = map[hash] = map[hash] || [null];
                items.push({
                    owner:owner,
                    callback:callback,
                    context:context,
                    meta:meta
                });
            },
            off:function (owner, callback, context) {
                var map = this._map;
                var hash = getHash(owner);
                var items = map[hash];
                if (items) {
                    if (callback == null) {
                        items.length = 1;
                    }
                    else if (context == null) {
                        each(items.slice(), function (item, index) {
                            if (item.callback == callback) {
                                items.splice(index, 1);
                                return false;
                            }
                        });
                    }
                    else {
                        each(items.slice(), function (item, index) {
                            if (item.callback == callback && item.context == context) {
                                items.splice(index, 1);
                                return false;
                            }
                        });
                    }
                }
            },
            prepare:function (event) {
                //if map.owner.disabled=true
                //payload.ignore=true;
                var map = this._map;
                var payload = {
                    event:event,
                    type:event.type,
                    map:map
                };

                var queue = payload.queue = [];
                var target = payload.target = event.target || event.srcElement || document;
                for (var parent = target; parent; parent = parent.parentNode) {
                    var entry = map[getHash(parent)];
                    if (entry) {
                        queue.push(entry);
                    }
                }
                return payload;
            },
            handle:function (payload) {
                if (!payload.ignore) {
                    var event = payload.event;
                    var eventClass = eventClasses[event.type] || fruit.Event;
                    var eventObj = new eventClass(event);
                    each(payload.queue, function (entry) {
                        each(entry, function (item) {
                            if (item) {
                                eventEngine.process(item, eventObj);
                                if (!eventObj.ignore && !eventObj.handled) {
                                    item.callback.call(item.context || item.owner, eventObj);
                                }
                            }
                        });
                    });
                }
            }
        }
    });
    var SimulatedEventHandler = fruit.define(EventHandler, {
        properties:{
            replace:{
                value:false
            }
        },
        methods:{
            init:function (type, actualType, capture, replace) {
                this.inherited(type, capture);
                this._actualType = actualType;
                this._replace = replace || false;
            },
            activate:function () {
                if (!this._active) {
                    this._active = true;
                    if (!this._replace) {
                        eventEngine.addEventListener(document, this._type, eventEngine.dispatch, this._capture);
                        eventEngine._handlerMap[this._type] = this;
                    }
                    eventEngine.addEventListener(document, this._actualType, eventEngine.dispatch);
                    eventEngine._handlerMap[this._actualType] = this;
                }
            },
            deactivate:function () {
                if (this._active) {
                    this._active = false;
                    if (!this._replace) {
                        eventEngine.removeEventListener(document, this._type, eventEngine.dispatch, this._capture);
                        eventEngine._handlerMap[this._type] = null;
                    }
                    eventEngine.removeEventListener(document, this._actualType, eventEngine.dispatch);
                    eventEngine._handlerMap[this._actualType] = null;
                }
            },
            prepare:function (event) {
                var payload = this.inherited(event);
                payload.type = this._type;
                return payload;
            }
        }
    });
    var eventHandlers = {
        focus:new SimulatedEventHandler('focus', 'focusin', true, true),
        blur:new SimulatedEventHandler('blur', 'focusout', true, true)
    };
    var eventModules = {
        interval:{
            active:false,
            //TODO: change 'item' to ???
            process:function (meta, item, eventObj) {
                if (item.waiting) {
                    eventObj.ignore = true;
                }
                else {
                    item.waiting = true;
                    setTimeout(function () {
                        item.waiting = false;
                    }, meta);
                }
            }
        },
        //TODO: add callback function
        filter:{
            active:false,
            process:function (meta, item, eventObj) {
                var target = eventObj.target;
                var tag = meta.tag;
                var cls = meta.cls;
                if (typeof meta == 'function') {
                    return meta(item, eventObj);
                }
                if (tag && target.tagName.toLowerCase() != tag) {
                    eventObj.ignore = true;
                }
                if (cls) {
                    var className = target.className + ' ';
                    if (className.indexOf(cls) < 0) {
                        eventObj.ignore = true;
                    }
                }
            }
        }
    };
    var eventClasses = {
        'dbclick':fruit.MouseEvent,
        'click':fruit.MouseEvent,
        'mousedown':fruit.MouseEvent,
        'mouseup':fruit.MouseEvent,
        'mouseover':fruit.MouseEvent,
        'mousemove':fruit.MouseEvent,
        'mouseout':fruit.MouseEvent,
        'keyup':fruit.KeyboardEvent,
        'keydown':fruit.KeyboardEvent
    };
    var eventEngine = fruit.EventEngine = {
        init:function () {
            this._handlerMap = {};
        },
        addEventListener:(function () {
            if (document.addEventListener) {
                return function (el, type, fn, capture) {
                    el.addEventListener(type, fn, capture || false);
                };
            } else {
                return function (el, type, fn) {
                    el.attachEvent("on" + type, fn);
                };
            }
        })(),
        removeEventListener:(function () {
            if (document.removeEventListener) {
                return function (el, type, fn) {
                    el.removeEventListener(type, fn);
                };
            } else {
                return function (el, type, fn) {
                    el.detachEvent("on" + type, fn);
                };
            }
        })(),
        upon:function (owner, type, callback, context, meta) {
            var handler = eventHandlers[type];
            if (!handler) {
                handler = new EventHandler(type);
                this.registerEventHandler(type, handler);
            }
            handler.upon(owner, callback, context, meta);
            handler.activate();
        },
        on:function (owner, type, callback, context, meta) {
            var handler = eventHandlers[type];
            if (!handler) {
                handler = new EventHandler(type);
                this.registerEventHandler(type, handler);
            }
            handler.on(owner, callback, context, meta);
            handler.activate();
        },
        off:function (owner, type, callback, context) {
            var handler = eventHandlers[type];
            if (handler) {
                handler.off(owner, callback, context);
            }
        },
        activateModule:function (name, inactive) {
            var module = eventModules[name];
            if (module) {
                module.active = !inactive;
            }
        },
        registerEventModule:function (name, eventModule) {
            eventModules[name] = eventModule;
        },
        registerEventHandler:function (type, eventHandler) {
            if (eventHandlers[type]) {
                eventHandlers[type].deactivate();
            }
            eventHandlers[type] = eventHandler;
        },
        registerEventClass:function (type, eventClass) {
            eventClasses[type] = eventClass;
        },
        dispatch:function (event) {
            var handlerMap = eventEngine._handlerMap;
            var type = event.type;
            var handler = handlerMap[type];
            if (handler) {
                var payload = handler.prepare(event);
                handler.handle(payload);
            }
        },
        process:function (item, eventObj) {
            each(item.meta, function (value, key) {
                var module = eventModules[key];
                if (module && module.active) {
                    module.process(value, item, eventObj);
                }

            });
        }
    };
    eventEngine.init();
    global.eventEngine = eventEngine;
    global.each = each;
}(fruit, window, document, undefined));